// HEM user intrface


// ----------------------------------------------------------------------------

#include "stdafx.h"

#include "Resources\DSymLoadVersion.h"
#include "DSymLoadImpl.h"

#include "DSymLoadUi.h"

// ----------------------------------------------------------------------------

#define DSYMLOAD_CAPTION "DSymLoad: "

// ----------------------------------------------------------------------------

namespace DSymLoad
{

// ----------------------------------------------------------------------------

namespace
{

// ----------------------------------------------------------------------------

enum
{
    MAX_VALUE_DISPLAY_CHARS = 28,
};

// ----------------------------------------------------------------------------

CStringA FormatValue(const CStringA csKey, CStringA csValue)
{
    if (csValue.GetLength() > MAX_VALUE_DISPLAY_CHARS)
        csValue = csValue.Left(MAX_VALUE_DISPLAY_CHARS - 1) + '~';
    return csKey + csValue;
}

// ----------------------------------------------------------------------------

CStringA FormatValue(const CStringA csKey, bool bValue)
{
    return FormatValue(csKey, CSettings::FormatBool(bValue));
}

// ----------------------------------------------------------------------------

void ShowHemWindow(
    __in HEM_BYTE *szTitle,
    std::list<CStringA> &WindowContent,
    HEM_UINT *returnFnKey = nullptr,
    HEM_FNKEYS *fnKeys = nullptr
)
{
    _ASSERT(!WindowContent.empty());

    int nWidth = 0;
    for (const auto &csLine : WindowContent)
        nWidth = max(nWidth, csLine.GetLength());

    std::vector<HEM_BYTE *> WindowLines(WindowContent.size());

    std::transform(
        WindowContent.begin(),
        WindowContent.end(),
        WindowLines.begin(),
        [] (CStringA &csLine) { return reinterpret_cast<HEM_BYTE *>(csLine.GetBuffer(csLine.GetLength())); }
    );

    struct CReleaseBufferGuard
    {
        explicit CReleaseBufferGuard(std::list<CStringA> &WindowContent)
            : m_WindowContent(WindowContent)
        {
        }
        ~CReleaseBufferGuard()
        {
            std::for_each(
                m_WindowContent.begin(),
                m_WindowContent.end(),
                [] (CStringA &csLine) { csLine.ReleaseBuffer(); }
            );
        }
        std::list<CStringA> &m_WindowContent;
    };
    CReleaseBufferGuard ReleaseBufferGuard(WindowContent);

    HEM_UINT localFnKey;
    if (!returnFnKey)
        returnFnKey = &localFnKey;

    ::HiewGate_Window(
        szTitle,
        &WindowLines[0],
        WindowLines.size(),
        nWidth,
        fnKeys,
        returnFnKey);
}

// ----------------------------------------------------------------------------

void ShowHelp()
{
    std::list<CStringA> WindowContent;
    WindowContent.push_back(
        std::move(CStringA(DSYMLOAD_DESCRIPTION_STR))
    );
    WindowContent.push_back(
        std::move(CStringA("Version: ") + DSYMLOAD_VERSION_BUILD_STR)
    );
    WindowContent.push_back(std::move(CStringA("Parameters:")));
    WindowContent.push_back(std::move(CStringA(" `Override if name exist' <F2>:")));
    WindowContent.push_back(std::move(CStringA("  Override all existing names if was address or name conflict")));
    WindowContent.push_back(std::move(CStringA("")));
    WindowContent.push_back(std::move(CStringA(" `Processing name collisions' <F3>:")));
    WindowContent.push_back(std::move(CStringA("  If find a symbol with existing name but a different address: ")));
    WindowContent.push_back(std::move(CStringA("  to name added address as suffix")));
    WindowContent.push_back(std::move(CStringA("")));
    WindowContent.push_back(std::move(CStringA(" `No CPP names style' <F4>:")));
    WindowContent.push_back(std::move(CStringA("  All C++ decorated symbols containing the symbol separator \"::\"")));
    WindowContent.push_back(std::move(CStringA("  are replaced by \"__\"")));
    WindowContent.push_back(std::move(CStringA("")));
    WindowContent.push_back(std::move(CStringA(" `Undecorate symbol names' <F5>:")));
    WindowContent.push_back(std::move(CStringA("  All symbols are presented in undecorated form")));
    WindowContent.push_back(std::move(CStringA("")));
    WindowContent.push_back(std::move(CStringA(" `Symbol server path' <F6>:")));
    WindowContent.push_back(std::move(CStringA("  The path, or series of paths separated by a semicolon (;), ")));
    WindowContent.push_back(std::move(CStringA("  that is used to search for symbol files. ")));
    WindowContent.push_back(std::move(CStringA("  If not specified (is empty) - attempts to form a symbol path")));
    WindowContent.push_back(std::move(CStringA("  from the following sources:")));
    WindowContent.push_back(std::move(CStringA("    - current working directory of the application")));
    WindowContent.push_back(std::move(CStringA("    - _NT_SYMBOL_PATH environment variable")));
    WindowContent.push_back(std::move(CStringA("    - _NT_ALTERNATE_SYMBOL_PATH environment variable")));
    WindowContent.push_back(std::move(CStringA("")));
    WindowContent.push_back(std::move(CStringA(" `DbgHelp.dll path' <F7>:")));
    WindowContent.push_back(std::move(CStringA("  The path to library dbghelp.dll and symsrv.dll. Usually this")));
    WindowContent.push_back(std::move(CStringA("  is the path to installed ")));
    WindowContent.push_back(std::move(CStringA("  \"Debugging Tools for Windows 32-bit Version\"")));
    WindowContent.push_back(std::move(CStringA("")));
    WindowContent.push_back(std::move(CStringA(" `Save' <F8>: Save current settings to DSymLoad.ini file")));
    WindowContent.push_back(std::move(CStringA(" `Load' <F9>: Override all settings from DSymLoad.ini file")));
    WindowContent.push_back(std::move(CStringA("IMPORTANT: Setting changes are saved only by pressing the <F8>")));

    static HEM_BYTE szTitle[] = DSYMLOAD_CAPTION "Help";
    ShowHemWindow(szTitle, WindowContent);
}


// ----------------------------------------------------------------------------

void GetStringSetting(
    __in HEM_BYTE *szTitle,
    __inout CSettings &Settings,
    __in const CStringA &(CSettings::*pGetMethod)()const,
    __in void(CSettings::*pSetMethod)(const CStringA &)
)
{
    const CStringA csValue = (Settings.*pGetMethod)();

    const auto nSize = max(csValue.GetLength(), MAX_PATH);
    std::vector<HEM_BYTE> Buffer(nSize + 1, '\0');
    std::copy(
        csValue.GetString(),
        csValue.GetString() + min(csValue.GetLength(), MAX_PATH),
        Buffer.begin());

    const auto nResult = HiewGate_GetString(szTitle, &Buffer[0], nSize);
    if (HEM_INPUT_CR == nResult)
        (Settings.*pSetMethod)(CStringA(&Buffer[0]));
}

// ----------------------------------------------------------------------------

}

// ----------------------------------------------------------------------------

void ShowMainWindow(__inout CSettings &Settings)
{
    static HEM_BYTE szKeys[] = 
//      "123456789ABC|"
        "111111111001|"
//      "F1____F2____F3____F4____F5____F6____F7____F8____F9____F10___F11___F12___"
        "Help  Over. NameClNoCPP Undec Sym   Lib   Sv.SetLd.Set            Load S";
    static HEM_BYTE szKeysEmpty[] = "";
    static HEM_FNKEYS fnKeys =
    {
        szKeys,
        szKeysEmpty, szKeysEmpty, szKeysEmpty
    };


    for (; ; )
    {
        std::list<CStringA> WindowContent;
        WindowContent.push_back(
            std::move(FormatValue("Override if name exist  : ", Settings.IsOverrideIfExist()))
        );
        WindowContent.push_back(
            std::move(FormatValue("Process name collision  : ", Settings.IsProcessNameCollision()))
        );
        WindowContent.push_back(
            std::move(FormatValue("No CPP names style      : ", Settings.IsNoCpp()))
        );
        WindowContent.push_back(
            std::move(FormatValue("Undecorate symbol names : ", Settings.IsUndName()))
        );
        WindowContent.push_back(
            std::move(FormatValue("Symbol server path      : ", Settings.GetSymPath()))
        );
        WindowContent.push_back(
            std::move(FormatValue("DbgHelp.dll path        : ", Settings.GetLibPath()))
        );
        WindowContent.push_back(std::move(CStringA(" Press <F12> for load symbols")));
        
        HEM_UINT retFnKey = 0;
        {
            static HEM_BYTE szTitle[] = DSYMLOAD_CAPTION "Load Options";
            /* INT nResult = */ShowHemWindow(
                szTitle,
                WindowContent,
                &retFnKey,
                &fnKeys);
        }

        switch (retFnKey)
        {
        case HEM_FNKEY_F1:
            ShowHelp();
            break;

        case HEM_FNKEY_F2:
            Settings.SetOverrideIfExist(!Settings.IsOverrideIfExist());
            break;

        case HEM_FNKEY_F3:
            Settings.SetProcessNameCollision(!Settings.IsProcessNameCollision());
            break;

        case HEM_FNKEY_F4:
            Settings.SetNoCpp(!Settings.IsNoCpp());;
            break;

        case HEM_FNKEY_F5:
            Settings.SetUndName(!Settings.IsUndName());
            break;

        case HEM_FNKEY_F6:
            {
                static HEM_BYTE szTitle[] = "New symbols path:";
                GetStringSetting(szTitle, Settings, &CSettings::GetSymPath, &CSettings::SetSymPath);
            }
            break;

        case HEM_FNKEY_F7:
            {
                static HEM_BYTE szTitle[] = "New DbgHelp library path:";
                GetStringSetting(szTitle, Settings, &CSettings::GetLibPath, &CSettings::SetLibPath);
            }
            break;

        case HEM_FNKEY_F8:
            Settings.Save();
            break;

        case HEM_FNKEY_F9:
            Settings.Load();
            break;

        case HEM_FNKEY_F12:
            {
                HIEWGATE_GETDATA HiewData;
                INT nResult = HiewGate_GetData(&HiewData);
                if (HEM_OK != nResult)
                    throw MakeFunctionCallError("HiewGate_GetData", "...", nResult);

                Do(Settings, CStringA(&HiewData.filename[0]));
            }
            // and return
        default:
            return;
        }
    }

}

// ----------------------------------------------------------------------------

void ShowError(__in const CError &Error)
{
    static_assert(ERROR_SUCCESS == S_OK, "ERROR_SUCCESS == S_OK");
    static_assert(ERROR_SUCCESS == NO_ERROR, "ERROR_SUCCESS == NO_ERROR");
    static_assert(ERROR_SUCCESS == HEM_OK, "ERROR_SUCCESS == HEM_OK");

    std::list<CStringA> WindowContent;
    WindowContent.push_back(Error.m_csDescription.GetString());

    if (Error.m_nErrorCode)
    {
        {
            CStringA csError = "Error ";
            if (static_cast<long>(Error.m_nErrorCode) < 0)
            {
                csError.AppendFormat("0x%x", Error.m_nErrorCode);
            }
            else
            {
                csError.AppendFormat("%d", Error.m_nErrorCode);
            }
            WindowContent.push_back( std::move(csError) );
        }

        PSTR szMessage = nullptr;
        const auto FormatResult =
            ::FormatMessageA(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                nullptr,
                Error.m_nErrorCode,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                reinterpret_cast<PSTR>(&szMessage),
                0,
                nullptr);
        if (FormatResult)
        {
            std::unique_ptr<CHAR, decltype(&::LocalFree)> MessageHolder(szMessage, &::LocalFree);

            CStringA csMessage(szMessage, FormatResult);

            int nPos = 0;
            CStringA csSubString = csMessage.Tokenize("\r\n", nPos);
            for (; !csSubString.IsEmpty(); csSubString = csMessage.Tokenize("\r\n", nPos))
                WindowContent.push_back( std::move(csSubString) );
        }
    }

    static HEM_BYTE szTitle[] = DSYMLOAD_CAPTION "Error";
    ShowHemWindow(szTitle, WindowContent);
}

// ----------------------------------------------------------------------------

}   // namespace DSymLoad

// ----------------------------------------------------------------------------

